import useApp from '@/store/app'
import useChannel from '@/store/channel'
import jsonBig from 'json-bigint'
import useEventBus from '@/use/use-event-bus'
import useUser from '@/store/user'
import msgAudio from '@/assets/audio/new_message.mp3'
import { handelMessageToString } from '@/utils/dialog-utils'
import { specificationPath } from '@/utils/index'
import { removeToken } from '@/utils/auth'

const eventBus = useEventBus()

const audio = document.createElement('audio')
audio.src = msgAudio

function useWebSocket(websocketUrl, options) {
  const { onMessage, heartbeat, autoReconnect, onError, immediate = true } = options
  let pongTimeoutWait = undefined
  let heartbeatTimeout = undefined
  let retried = 0
  let explicitlyClosed = false
  const wsRef = ref(undefined)

  function init() {
    const ws = new WebSocket(websocketUrl)
    wsRef.value = ws

    ws.onopen = () => {
      retried = 0
    }

    ws.onmessage = (e) => {
      if (heartbeat) {
        resetHeartbeat()
      }
      onMessage?.(ws, e)
    }

    ws.onclose = () => {
      if (!explicitlyClosed && autoReconnect) {
        const { retries, delay, onFailed } = autoReconnect
        let currentTime = new Date()
        if (typeof retries === 'number' && (retries < 0 || retried < retries)) {
          retried += 1
          console.warn(currentTime.getTime(), retried, delay, 'retry connect')
          setTimeout(init, delay)
        } else if (typeof retries === 'function' && retries()) {
          console.warn(currentTime.getTime(), retried, 'retry connect')
          setTimeout(init, delay)
        } else {
          onFailed?.()
        }
      }
    }

    ws.onerror = (e) => {
      console.error('WebSocket error:', e)
      onError?.(ws, e)
    }

    if (heartbeat) {
      const { message, interval, pongTimeout } = heartbeat
      heartbeatTimeout = setInterval(() => {
        if (ws.readyState !== WebSocket.CLOSED && ws.readyState !== WebSocket.CLOSING) {
          ws.send(message)
          if (pongTimeout !== undefined) return
          pongTimeoutWait = setTimeout(() => {
            close()
          }, pongTimeout)
        } else {
          clearInterval(heartbeatTimeout)
        }
      }, interval)
    }
  }

  function resetHeartbeat() {
    clearTimeout(pongTimeoutWait)
    pongTimeoutWait = undefined
  }

  function open() {
    close()
    explicitlyClosed = false
    retried = 0
    init()
  }

  function send(data) {
    if (wsRef.value?.readyState === WebSocket.OPEN) {
      wsRef.value.send(data)
    }
  }

  function close() {
    if (!wsRef.value) return
    clearInterval(heartbeatTimeout)
    clearTimeout(pongTimeoutWait)
    heartbeatTimeout = undefined
    pongTimeoutWait = undefined
    explicitlyClosed = true
    wsRef.value.close()
    wsRef.value = undefined
  }

  if (immediate) open()

  return {
    send,
    open,
    close,
    ws: computed(() => {
      return wsRef.value
    }).value,
  }
}

function createWebsocket(key, options = {}) {
  const { url, dataChange } = options
  if (!url) return
  const host = window.location.host
  const ws = window.location.protocol === 'https:' ? 'wss' : 'ws'
  const baseUrl = `${ws}://${host}${process.env.VUE_APP_API_URL}`
  const websocketUrl = specificationPath(`${baseUrl}/${url}`)
  return useWebSocket(websocketUrl, {
    heartbeat: {
      message: JSON.stringify({ action: 'ping' }),
      interval: 30000,
      pongTimeout: 20000,
    },
    autoReconnect: {
      retries: 100,
      delay: 10000,
      onFailed() {
        const router = useRouter()
        sessionStorage.clear()
        localStorage.clear()
        removeToken()
        router.push('/login')
      },
    },
    onMessage: dataChange,
  })
}

export function createChatWebSocket() {
  const appStore = useApp()
  const channelStore = useChannel()
  const ws = createWebsocket('chat', {
    url: '/chat-im-ws/chat',
    dataChange: (ws, { data: v }) => {
      if (process.env.NODE_ENV === 'development') {
        console.log(v)
      }
      const { event, data, clientAction } = jsonBig({ storeAsString: true }).parse(v)
      // 更新时间差
      if (clientAction === 'ping') {
        appStore.setServerTime(data.serverTime)
      }
      switch (event) {
        case 'auth_req':
          {
            appStore.imWebsocket.send(
              JSON.stringify({
                action: 'auth_req',
                data: {
                  token: appStore.token,
                },
              })
            )
          }
          break
        case 'message_posted':
          {
            handleMessagePosted(data)
            const { id, pinnedFlag } = channelStore.channelList.find((i) => i.id === data.channelId)
            handleMessageNotification(data)
            channelStore.topChannel(id, pinnedFlag)
          }
          break
        case 'message_file_uploaded':
          channelStore.updateFileStatus(data)
          break
        case 'message_read':
          channelStore.addChannelMsg(data)
          break
        case 'message_reaction':
          channelStore.updateMessage(
            { channelId: data.channelId, id: data.msgId },
            'metaData.reactions',
            data.reactions
          )
          break
        case 'message_edit':
          channelStore.updateMessage(data, 'content', data.content)
          channelStore.updateMessage(data, 'metaData.status.editFlag', data.metaData.status.editFlag)
          break
        case 'message_top':
          channelStore.topMessage(data)
          break
        case 'message_cancel_top':
          channelStore.topMessage(data, false)
          break
        case 'message_tag':
          channelStore.updateMessage(data, 'metaData.status.tagFlag', data.metaData.status.tagFlag)
          break
        case 'message_pinned':
          channelStore.updateMessage(data, 'metaData.status.pinnedFlag', true)
          break
        case 'message_unpinned':
          channelStore.updateMessage(data, 'metaData.status.pinnedFlag', false)
          break
        case 'member_delete':
          channelStore.updateDialog(data.channelId, 'removeFlag', true)
          break
        case 'member_add':
          channelStore.addChannelList(data)
          break
        case 'member_channel_pinned':
          channelStore.updateDialog(data.channelId, 'pinnedFlag', data.pinnedFlag)
          channelStore.topChannel(data.channelId, data.pinnedFlag)
          break
        case 'member_channel_dnd':
          channelStore.updateDialog(data.channelId, 'dndFlag', data.dndFlag)
          break
        case 'channel_manager_change':
          data.managers.forEach((i) => channelStore.updateMember(i))
          break
        case 'channel_delete':
          channelStore.updateDialog(data.id, 'deleteFlag', true)
          break
        case 'message_text_tag':
          channelStore.updateMessage(data, 'metaData.textTags', data.metaData.textTags)
          break
        case 'member_update_notify':
          channelStore.updateMember(data)
          break
        case 'channel_created':
          {
            channelStore.addChannelList(data)
            channelStore.topChannel(data.channelId, data.pinnedFlag)
          }
          break
        case 'channel_update':
          {
            const props = ['name', 'icon']
            props.forEach((prop) => {
              channelStore.updateChannel(data.id, prop, data[prop])
            })
            eventBus.emit('channelCreated', data)
          }
          break
        default:
      }
    },
  })
  appStore.setImWebsocket(ws)
  return ws
}

// 消息websocket
// const websocket = useWebSocket(websocketUrl, {
//   heartbeat: {
//     message: JSON.stringify({ action: 'ping' }),
//     interval: 30000,
//     pongTimeout: 20000,
//   },
//   autoReconnect: {
//     retries: 720,
//     delay: 10000,
//     onFailed() {
//       console.error('Failed to connect WebSocket after 720 retries')
//     },
//   },
// })

// 处理消息发送
function handleMessagePosted(data) {
  const channelStore = useChannel()
  // 1-用户消息,2-系统消息,3-机器人消息,4-应用消息
  if (data.type === 2 || data.type === 1 || data.type === 3) {
    channelStore.addChannelMsg(data)
  }
  eventBus.emit('dialogScrollBottom')
}

// 处理消息通知
function handleMessageNotification(data) {
  const channelStore = useChannel()
  const userStore = useUser()
  const isLeave = useDocumentVisibility()
  if (isLeave.value === 'hidden' && userStore.userId !== data.createBy) {
    let body = handelMessageToString(data)
    audio.play()
    const channel = channelStore.channelList.find((i) => i.id === data.channelId)
    const n = new Notification(data.createUser, {
      dir: 'auto',
      body,
      data: {
        channelId: data.channelId,
      },
      icon: (channel && channel.icon) || 'https://www.soflyit.com/notify-logo/logo.png',
      lang: 'zh-Hans-CN',
      renotify: true,
      tag: 'message',
      channelId: data.channelId,
    })
    n.onclick = (e) => {
      eventBus.emit('jumpChannel', e.target.data)
    }
  }
}

export function createFileManageWebSocket() {
  const appStore = useApp()
  return createWebsocket('fileManage', {
    url: `/chat-dx/ws-upload/${appStore.token}`,
    dataChange: (ws, { data: v }) => {
      const { type, data } = jsonBig({ storeAsString: true }).parse(v)
      switch (type) {
        case 'upload-progress':
          eventBus.emit('fileUploadProgress', data)
          break
        default:
      }
    },
  })
}

// 文档库websocket
// const dxWebsocket = useWebSocket(dxWebsocketUrl, {
//   heartbeat: {
//     message: JSON.stringify({ action: 'ping' }),
//     interval: 30000,
//     pongTimeout: 20000,
//   },
//   autoReconnect: {
//     retries: 720,
//     delay: 10000,
//     onFailed() {
//       console.error('Failed to connect WebSocket after 720 retries')
//     },
//   },
// })

// export { dxWebsocket }
// export default websocket
